#include <asm/io.h>
#include <asm/regs.h>
#include <asm/system.h>
#include <cnix/driver.h>
#include <cnix/kernel.h>

#define P2M_TIMEOUT 32000

static void pre_write(void)
{
	mstate_t ms;

	mstart(&ms);

	do{
		if (!(inb_p(0x64) & 0x02))
		{
			return;
		}
	} while(melapsed(&ms) < P2M_TIMEOUT);

	printk("pre_write failed\n");
}

static void pre_read(void)
{
	mstate_t ms;

	mstart(&ms);

	do{
		if (inb_p(0x64) & 0x01)
		{
			return;
		}
	} while(melapsed(&ms) < P2M_TIMEOUT);

	printk("pre_read failed\n");
}

#define Y_OVERFLOW	0x80
#define X_OVERFLOW	0x40
#define Y_SIGN		0x20
#define X_SIGN		0x10	
#define BTN_MIDDLE	0x04
#define BTN_RIGHT	0x02
#define BTN_LEFT	0x01

static void ps2mouse_intr(struct regs_t * regs)
{
	unsigned long flags;
	unsigned char flag, ybyte, xbyte;
	int y, x;

	lock(flags);

	flag = inb_p(0x60);
	xbyte = inb_p(0x60);
	ybyte = inb_p(0x60);

	unlock(flags);

	if (flag & (Y_OVERFLOW | X_OVERFLOW))
	{
		return;		
	}

	y = ybyte;
	if (flag & Y_SIGN)
	{
		y |= 0xffffff00;
	}

	x = xbyte;
	if (flag & X_SIGN)
	{
		x |= 0xffffff00;
	}
}

void ps2mouse_init(void)
{
	unsigned long flags;
	unsigned char status;

	lock(flags);

	/* discard any byte */
	while (inb_p(0x64) & 0x01)
	{
		inb_p(0x60);
	}

	/* enable ps2 mouse */
	pre_write();
	outb_p(0xa8, 0x64);

	/* enable ps2 mouse interrupt */
	pre_write();
	outb_p(0x20, 0x64);
	pre_read();
	status = inb_p(0x60);
	pre_write();
	outb_p(0x60, 0x64);

	pre_write();
	outb_p(status | 0x02, 0x60);

	/* set defaults */
	pre_write();
	outb_p(0xd4, 0x64);
	pre_write();
	outb_p(0xf6, 0x60);

	pre_read();
	inb_p(0x60);

	/* enable data reporting */
	pre_write();
	outb_p(0xd4, 0x64);
	pre_write();
	outb_p(0xf4, 0x60);

	pre_read();
	inb_p(0x60);

	unlock(flags);

	put_irq_handler(12, ps2mouse_intr);
}

#include <cnix/modconfig.h>

#ifdef PS2MOUSE_MODULE

int module_enter(void)
{
	return 1;
}

int module_exit(void)
{
	return 1;
}

#endif
